﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using FinPlusCommon;
using u = FinPlusUtility.Utility;

namespace FinPlusComponents
{
    public class FinPlusQuoteBuySells : ConcurrentDictionary<string, FinPlusQuoteBuySell> { }

    public class FinPlusQuoteBuySell: FinPlusComponent
    {
        public FinPlusQuoteWrapper BestQuote { get; private set; }
        public IFunction MarketLevel { get { return _marketLevel; } }
        public string Instrument { get; private set; }
        public string BuySell { get; private set; }

        private enum Params { LevelUpdate, Instrument, LinkedCurve, PublishPriceAction, ReceiveAction, Market, BuySell, Source, InstrumentType, Level, DateTime, RateType, Book, Size, Trade, Trader, Quote, QuoteSource, QuoteStyle, VM }
        private readonly IFunction _instrument;
        private IFunction _marketLevel, _quoteInterest;
        private readonly FinPlusQuoteSources _platforms;
        private readonly FinPlusQuoteSource _quoteSource;
        private ConcurrentDictionary<string, FinPlusQuoteWrapper> _quotes, _publishQuotes, _spreadInstruments;
        private Action<IFunction> _publish;
        private readonly string _sourceName;

        //extra fields
        private IItem _quoteReceiveAction, _quote, _source, _spread, _mktToMkt;

        //construct
        public FinPlusQuoteBuySell(IFunction instrument, string buySell, string sourceName, Action<IFunction> publish = null, 
            FinPlusQuoteSources platforms = null, FinPlusQuoteSource quoteSource = null)
        {
            BuySell = buySell;
            Instrument = instrument[Params.Instrument.ToString()].ToString();

            _instrument = instrument;
            _sourceName = sourceName;
            _quoteReceiveAction = _instrument.Get(Params.ReceiveAction.ToString());
            _quotes = new ConcurrentDictionary<string, FinPlusQuoteWrapper>();
            _publishQuotes = new ConcurrentDictionary<string, FinPlusQuoteWrapper>();
            _quoteSource = quoteSource;
            _platforms = platforms;
            _publish = publish;
            _quoteInterest = new Function();
            _quote = _instrument.Add(string.Format("{0}{1}", _sourceName, BuySell), new Function(), _sourceName);
            //_size = _instrument.Add(string.Format("{0}{1}Size", _sourceName, BuySell), 0.0, _sourceName);
            //_quoteStyle = _instrument.Add(string.Format("{0}{1}Style", _sourceName, BuySell), "N/A", _sourceName);
            AddMarketLevel();
            //if (!_sourceName.Equals(Params.VM.ToString()) && !_sourceName.Equals(Params.Trade.ToString())) _depth = instrument.Add(string.Format("{0}{1}Depth", sourceName, buySell), 0, sourceName);
            if (_sourceName.Equals(Params.VM.ToString())) _source = instrument.Add(string.Format("{0}{1}Source", sourceName, buySell), "N/A", sourceName);
            if (_sourceName.Equals(Params.Trade.ToString())) { _spread = instrument.Add(string.Format("Spread{0}", buySell), "N/A", sourceName); _mktToMkt = instrument.Add(string.Format("MktToMkt{0}", buySell), 0.0, sourceName); }
        }

        //common control interface
        public override void Dispose()
        {
            QuoteCancelPublish();
        }

        //public
        public void AddMarketLevel()
        {
            _marketLevel = new Function(_instrument[Params.RateType.ToString()].ToString());
            _marketLevel[Params.Market.ToString()] = "Live";//TODO source from config
            _marketLevel[Params.LinkedCurve.ToString()] = _instrument[Params.LinkedCurve.ToString()];
            _marketLevel[Params.Instrument.ToString()] = Instrument;
            _marketLevel[Params.BuySell.ToString()] = BuySell;
            _marketLevel[Params.Level.ToString()] = 0.0;
            _marketLevel[Params.Source.ToString()] = _sourceName;
            _marketLevel[Params.DateTime.ToString()] = DateTime.Now.Date;
        }

        public void AddQuoteInterests(IFunction[] quoteInterests)
        {
            if (quoteInterests.Count() < 1) return;//note currently only handles one interest keep UI simple for now

            _quoteInterest = quoteInterests[0];
            //_quote.SetValue(_quoteInterest);
        }

        //quote
        public bool Quote(FinPlusQuoteWrapper quote, string source)
        {
            try
            {
                quote.Quote[Params.Book.ToString()] = _quoteInterest[Params.Book.ToString()];
                if (_quotes.TryAdd(quote.QuoteRef, quote))
                    if (BestQuote == null || BestQuote.IsBest(quote))
                        return UpdateBestQuote(quote, source);

                return false;
            }
            catch
            {
                throw; //source level manages exceptions
            }
        }

        public bool QuoteCancel(FinPlusQuoteWrapper quote, string source)
        {
            try
            {
                FinPlusQuoteWrapper q;
                if (_quotes.TryRemove(quote.QuoteRef, out q) && BestQuote != null && quote.QuoteRef == BestQuote.QuoteRef)
                    return UpdateBestQuote(FindBestQuote(), source);

                return false;
            }
            catch
            {
                throw; //source level manages exceptions
            }
        }

        public void QuotePublish(FinPlusQuoteWrapper best)
        {
            try
            {
                QuoteCancelPublish();

                if (best == null || _quoteSource == null || _platforms == null) return;
                if (!_quoteSource.IsReady()) _quoteSource.Stop();
                if (!_quoteSource.IsOn) return;

                foreach (var platform in _platforms.Values)
                {
                    if (platform.IsOn && !platform.SourceName.Equals(Params.VM.ToString()))
                    {
                        var publishQuote = best.Clone(u.Guid(), Instrument, platform.SourceName, "QuotePublish");
                        if (_publishQuotes.TryAdd(publishQuote.QuoteRef, publishQuote))
                            _publish(publishQuote.Quote);
                    }
                }
            }
            catch
            {
                throw; //source level manages exceptions
            }
        }

        public void QuoteCancelPublish()
        {
            try
            {
                if (_publishQuotes != null)
                {
                    foreach (var cancel in _publishQuotes.Values)
                    {
                        var cancelQuote = cancel.Clone(u.Guid(), Instrument, cancel.SourceName, "QuoteCancelPublish");
                        _publish(cancelQuote.Quote);
                    }
                }

                _publishQuotes.Clear();
            }
            catch
            {
                throw; //source level manages exceptions
            }
        }

        public void QuoteExecutePublish(FinPlusQuoteWrapper quote)
        {
            if (_publish != null)
                _quoteReceiveAction.Set(quote.Quote);
        }

        public void Spread(FinPlusQuoteWrapper bestQuote, FinPlusQuoteSpread spread)
        {
            try
            {
                FinPlusQuoteWrapper q = null;
                _quotes.TryRemove(spread.SpreadType.Equals(Params.Instrument.ToString()) ? Params.Instrument.ToString() : spread.SpreadTo, out q);

                if (bestQuote != null)
                    bestQuote.Spread = spread;

                if (spread.SpreadType.Equals(Params.Instrument.ToString()))
                    SpreadInstrument(bestQuote);
                else if (spread.SpreadType.Equals(Params.QuoteSource.ToString()))
                    SpreadSource(bestQuote);

                //get trader quote
                var best = FindSpreadQuote();

                //update view
                if (UpdateBestQuote(best, Params.Trader.ToString()))
                    QuotePublish(best);//publish new quotes

                //update mkt to mkt
                if (spread.IsMktToMkt)
                    UpdateMktToMkt(FindSpreadQuote(spread.IsMktToMkt));
            }
            catch
            {
                throw; //source level manages exceptions
            }
        }

        //private
        private void ClearBestQuote()
        {
            _quote.Set(new Function());
            //_size.SetValue(string.Empty);
            //_quoteStyle.SetValue(string.Empty);
            if (_source != null) _source.Set(string.Empty);
            if (_spread != null) _spread.Set(string.Empty);
        }

        private FinPlusQuoteWrapper FindBestQuote()
        {
            try
            {
                FinPlusQuoteWrapper bestQuote = null;
                foreach (var quote in _quotes.Values)
                    if (bestQuote == null || bestQuote.IsBest(quote)) bestQuote = quote;

                return bestQuote;
            }
            catch
            {
                throw; //source level manages exceptions
            }
        }

        private FinPlusQuoteWrapper FindSpreadQuote(bool justMktToMkt = false)
        {
            try
            {
                FinPlusQuoteWrapper spreadSource = null;
                foreach (var quote in _quotes.Values)
                    if (!justMktToMkt || quote.IsMktToMkt)
                        if (spreadSource == null || !spreadSource.IsBest(quote))
                            spreadSource = quote;

                return spreadSource;
            }
            catch
            {
                throw;
            }
        }

        private void SpreadSource(FinPlusQuoteWrapper baseQuote)
        {
            try
            {
                if (baseQuote == null) return;
                var newQuote = baseQuote.QuoteFromSourceSpread(baseQuote.QuoteRef, Instrument, Params.Trade.ToString(), _quoteInterest[Params.QuoteStyle.ToString()].ToString(), _quoteInterest[Params.Size.ToString()].ToString());
                _quotes.TryAdd(baseQuote.Spread.SpreadTo, newQuote);
            }
            catch (Exception e)
            {
                throw new Exception(baseQuote.ToString(), e);
            }
        }

        private void SpreadInstrument(FinPlusQuoteWrapper baseQuote)
        {
            try
            {
                if (baseQuote == null) return;
                if (_spreadInstruments == null) _spreadInstruments = new ConcurrentDictionary<string, FinPlusQuoteWrapper>();

                double quote = 0, totalWeight = 0, totalAdj = 0;
                var spreadTo = new StringBuilder();

                _spreadInstruments[baseQuote.Instrument] = baseQuote;

                foreach (var spreadInstrument in _spreadInstruments.Values)
                {
                    quote += spreadInstrument.Value * spreadInstrument.Spread.Weight;
                    totalWeight += spreadInstrument.Spread.Weight;
                    totalAdj += spreadInstrument.Spread.Adjustment(BuySell);
                    spreadTo.Append(spreadInstrument.Instrument).Append("|");
                }

                if (totalWeight > 1.000000002 || totalWeight < 0.999999998) return;

                _quotes.TryAdd(Params.Instrument.ToString(), baseQuote.QuoteFromInstrumentSpread(baseQuote.QuoteRef, Instrument, Params.Trade.ToString(), spreadTo.ToString().TrimEnd('|'), quote, totalAdj, _quoteInterest[Params.QuoteStyle.ToString()].ToString(), _quoteInterest[Params.Size.ToString()].ToString()));
            }
            catch (Exception e)
            {
                throw new Exception(baseQuote.ToString(), e);
            }
        }

        private bool UpdateBestQuote(FinPlusQuoteWrapper bestQuote, string source)
        {
            try
            {
                bool isEqual = false;
                if (BestQuote != null && bestQuote != null)
                {
                    if (bestQuote.QuoteRef.Equals(BestQuote.QuoteRef))
                        return false;
                    isEqual = BestQuote.IsEqual(bestQuote);
                }

                BestQuote = bestQuote;
                if (bestQuote != null)
                {
                    _quote.Set(bestQuote.Quote);
                    //_size.SetValue(bestQuote.Size);
                    //_quoteStyle.SetValue(bestQuote.QuoteStyle);
                    if (_source != null) _source.Set(source);
                    if (_spread != null) _spread.Set(bestQuote.SpreadTo);
                }
                else
                    ClearBestQuote();

                //if (_depth != null) _depth.SetValue(_quotes.Count());

                if (isEqual) return false;

                return true;
            }
            catch (Exception e)
            {
                throw new Exception(bestQuote.ToString(), e);
            }
        }

        private void UpdateMktToMkt(FinPlusQuoteWrapper mktToMktQuote)
        {
            try
            {
                if (_mktToMkt == null || mktToMktQuote == null || _mktToMkt.Value.Equals(mktToMktQuote.Value.ToString())) return;

                _mktToMkt.Set(mktToMktQuote.Value.ToString());

                if (_marketLevel != null)
                {
                    _marketLevel.Get(Params.Level.ToString()).Set(mktToMktQuote.Value.ToString());
                    _instrument.Get(Params.LevelUpdate.ToString()).Set(_marketLevel);
                }
            }
            catch (Exception e)
            {
                throw new Exception(mktToMktQuote.ToString(), e);
            }
        }
    }
}
